// EventManager.js

export default class Event {
    private subscribes: {} = {};
    private m_emit_reference_count: number = 0;

    constructor() {
        // this.subscribes = {};
        // this.m_emit_reference_count = 0;
    }

    public on(name, func, target?) {
        if (!this.subscribes[name]) {
            this.subscribes[name] = [];
        }
        const subscription = { f: func, del: false, target: target };
        this.subscribes[name].push(subscription);

        return () => {
            this.off(name, func);
        };
    }

    public once(name, func, target?) {
        const unsubscribe = this.on(
            name,
            (...args: any[]) => {
                func.apply(target, args);
                unsubscribe();
            },
            target
        );
        return unsubscribe;
    }

    public emit(name, ...args) {
        ++this.m_emit_reference_count;
        const subscriptions = this.subscribes[name];
        if (subscriptions) {
            subscriptions.forEach((subscription) => {
                if (subscription.f && !subscription.del) {
                    subscription.f.apply(subscription.target, args);
                }
            });
        }
        --this.m_emit_reference_count;
        return;
    }

    public off(name, func) {
        const subscriptions = this.subscribes[name];
        if (subscriptions) {
            if (func) {
                subscriptions.forEach((subscription) => {
                    if (subscription.f === func) {
                        subscription.del = true;
                    }
                });
            } else {
                subscriptions.forEach((subscription) => {
                    subscription.del = true;
                });
            }
        }
        if (this.m_emit_reference_count === 0) {
            this.clear();
        }
    }

    private clear() {
        for (const name in this.subscribes) {
            this.subscribes[name] = this.subscribes[name].filter((v) => !v.del);
        }
    }
}
